#include "ThreadPool.h"
#include "WorkerThread.h"
#include <algorithm>


ThreadPool::ThreadPool(int nInitThreads, int nMaxThreads) :
    m_semThreads(0),
    m_semMaxThreads(1),
    m_nThreads(nInitThreads),
    m_nMaxThreads(nMaxThreads)
{
    m_listIdle.clear();
    m_listBusy.clear();
    CreateIdleThread(m_nThreads);
}

ThreadPool::~ThreadPool(void)
{
}

Thread* ThreadPool::GetIdleThread()
{
    m_mutexIdle.Lock();
    while (m_listIdle.size() ==0)
        m_condIdle.Wait(&m_mutexIdle);
    if (m_listIdle.size() > 0)
    {
        Thread* pThread = m_listIdle.front();
        m_mutexIdle.Unlock();
        return pThread;
    }
    // here is never reached, but for safe reason.
    m_mutexIdle.Unlock();
    return NULL;
}

void ThreadPool::CreateIdleThread(int nNumbers)
{
    m_mutexIdle.Lock();
    for (int i = 0; i < nNumbers; i++)
    {
        Thread* pThread = new WorkerThread();
        pThread->SetThreadPool(this);
        m_listIdle.push_back(pThread);
        pThread->Start();
    }
    m_mutexIdle.Unlock();
}

void ThreadPool::DeleteIdleThread(int nNumbers)
{
    m_mutexIdle.Lock();
    for (int i = 0; i < nNumbers; i++)
    {
        Thread* pThread = NULL;
        if (m_listIdle.size() > 0)
        {
            pThread = m_listIdle.front();
            m_listIdle.pop_front();
            pThread->Terminate();
            delete pThread;
        }
        else
            break;
    }
    m_mutexIdle.Unlock();
}

bool ThreadPool::RemoveThread(Thread* pThread)
{
    if (RemoveBusyThread(pThread))
        return true;
    if (RemoveIdleThread(pThread))
        return true;
    return false;
}

bool ThreadPool::RemoveBusyThread(Thread* pThread)
{
    bool flag = false;
    m_mutexBusy.Lock();
    list<Thread *>::iterator it;
    it = find(m_listBusy.begin(), m_listBusy.end(), pThread);
    if (it != m_listBusy.end())
    {
        //(*it)->Terminate();
        m_listBusy.erase(it);
        flag = true;
    }
    m_condBusy.Signal();
    m_mutexBusy.Unlock();
    return flag;
}

bool ThreadPool::RemoveIdleThread(Thread* pThread)
{
    bool flag = false;
    m_mutexIdle.Lock();
    list<Thread *>::iterator it;
    it = find(m_listIdle.begin(), m_listIdle.end(), pThread);
    if (it != m_listIdle.end())
    {
        //(*it)->Terminate();
        m_listIdle.erase(it);
        flag = true;
    }
    m_condIdle.Signal();
    m_mutexIdle.Unlock();
    return flag;
}

void ThreadPool::MoveToBusyList(Thread *pThread)
{
    m_mutexBusy.Lock();
    while (m_listBusy.size() == GetMaxThreadNumbers())
        m_condBusy.Wait(&m_mutexBusy);
    m_listBusy.push_back(pThread);
    m_mutexBusy.Unlock();

    m_mutexIdle.Lock();
    list<Thread *>::iterator it;
    it = find(m_listIdle.begin(), m_listIdle.end(), pThread);
    if (it != m_listIdle.end())
        m_listIdle.erase(it);
    m_mutexIdle.Unlock();

    // RemoveIdleThread(pThread);
}

void ThreadPool::MoveToIdleList(Thread *pThread)
{
    m_mutexIdle.Lock();
    m_listIdle.push_back(pThread);
    m_condIdle.Signal();
    m_mutexIdle.Unlock();

    m_mutexBusy.Lock();
    list<Thread *>::iterator it;
    it = find(m_listBusy.begin(), m_listBusy.end(), pThread);
    if (it != m_listBusy.end())
        m_listBusy.erase(it);
    m_condBusy.Signal();
    m_mutexBusy.Unlock();

    // RemoveBusyThread(pThread);
}

int ThreadPool::GetBusyThreadNumbers()
{
    int nNumbers = 0;
    m_mutexBusy.Lock();
    nNumbers = m_listBusy.size();
    m_mutexBusy.Unlock();
    return nNumbers;
}

int ThreadPool::GetIdleThreadNumbers()
{
    int nNumbers = 0;
    m_mutexIdle.Lock();
    nNumbers = m_listIdle.size();
    m_mutexIdle.Unlock();
    return nNumbers;
}

int ThreadPool::GetThreadNumbers()
{
    int nNumbers = 0;
    m_semThreads.Wait();
    nNumbers = m_nThreads;
    m_semThreads.Post();
    return nNumbers;
}

int ThreadPool::GetMaxThreadNumbers()
{
    int nNumbers = 0;
    m_semMaxThreads.Wait();
    nNumbers = m_nMaxThreads;
    m_semMaxThreads.Post();
    return nNumbers;
}

void ThreadPool::StartAll()
{
    list<Thread *>::iterator it;
    m_mutexIdle.Lock();
    for (it = m_listIdle.begin(); it != m_listIdle.end(); ++it)
    {
        (*it)->Start();
    }
    m_mutexIdle.Unlock();
    m_mutexBusy.Lock();
    for (it = m_listBusy.begin(); it != m_listBusy.end(); ++it)
    {
        (*it)->Start();
    }
    m_mutexBusy.Unlock();
}

void ThreadPool::TerminateAll()
{
    list<Thread *>::iterator it;
    m_mutexIdle.Lock();
    for (it = m_listIdle.begin(); it != m_listIdle.end(); ++it)
    {
        (*it)->Terminate();
    }
    m_mutexIdle.Unlock();
    m_mutexBusy.Lock();
    for (it = m_listBusy.begin(); it != m_listBusy.end(); ++it)
    {
        (*it)->Terminate();
    }
    m_mutexBusy.Unlock();
}

void ThreadPool::DeleteAll()
{
    list<Thread *>::iterator it;
    m_mutexIdle.Lock();
    for (it = m_listIdle.begin(); it != m_listIdle.end(); ++it)
    {
        (*it)->Terminate();
        delete *it;
    }
    m_mutexIdle.Unlock();
    m_mutexBusy.Lock();
    for (it = m_listBusy.begin(); it != m_listBusy.end(); ++it)
    {
        (*it)->Terminate();
        delete *it;
    }
    m_mutexBusy.Unlock();
}

void ThreadPool::Assign(Job *pJob)
{
    if (pJob == NULL)
    {
        //printf("Assign a NULL Job to threadpool!");
        return;
    }
    //printf("Assign a new Job: %x to threadpool!", pJob);

    m_mutexBusy.Lock();
    while (m_listBusy.size() == GetMaxThreadNumbers())
        m_condBusy.Wait(&m_mutexBusy);
    m_mutexBusy.Unlock();

    //if ((GetIdleThreadNumbers() == 0) && (GetBusyThreadNumbers() < GetMaxThreadNumbers()))
    //{
    //    //try to create 1/10 of busyNum new threads, at lease 1 thread;
    //    int nCreateNum = GetBusyThreadNumbers() / 10 > 0 ? GetBusyThreadNumbers() / 10 :1;
    //    if (nCreateNum > GetMaxThreadNumbers() - GetBusyThreadNumbers())
    //        nCreateNum = GetMaxThreadNumbers() - GetBusyThreadNumbers();
    //    CreateIdleThread(nCreateNum);
    //}

    Thread* pThread = GetIdleThread();
    if(pThread != NULL)
    {
        MoveToBusyList(pThread);
        pThread->SetThreadPool(this);
        pJob->SetWorkThread(pThread);
        pThread->SetJob(pJob);
    }
    else
    {
        printf("Assign a Job failed!");
    }

}


